C++ std::{bind and tuple and function} simple implementation

  • 2020-07-21 09:39:43
  • OfStack

Basic logical thinking

The first is to implement function, which is relatively simple, and override operator(), where you only implement the wrapper around the function pointer

The second is the implementation of tuple, which is rather convoluted. It takes the first parameter through the template, and then continues to generate and inherit tuple with the remaining parameters, which is a recursive idea

With tuple comes get(), which is even more convoluted. First, a similar implementation is needed to obtain the value type and tuple type of tuple, and then obtain the corresponding level of value by casting

Next up is bind. The first thing you need to do is to save the list of parameters when you created it

The trick is to run the function and take the value of the corresponding tuple at the corresponding position, or in a similar way, through the specialized template, the formula is < n, indexs... > = > < n - 1, n - 1, indexs... > For example, 3 will generate 0, 0, 1, 2 so discard the first one and use it to expand the tuple and pass it to the function pointer

The most important thing is how to implement placeholders, which is simply to increase the parameters passed in from operator() in the previous step, and make the tuple r_args, and then bring in a _unwrap_tuple class, which will override operator[] according to the data structure passed in, if it's _placeholders < index > Then take the corresponding index location of r_args, otherwise return will be directly used

code

Not to mention, or directly put the code, just for reference, there are bad places to write light spray


/*
 * Author: SpringHack - springhack@live.cn
 * Last modified: 2020-02-19 10:16:17
 * Filename: main.cpp
 * Description: Created by SpringHack using vim automatically.
 */
#include <iostream>

namespace dosk { // begin namespace dosk

// function
template <typename... T>
class function;

template <typename Result, typename... Args>
class function<Result(Args...)> {
 private:
  Result (*function_)(Args...);
 public:
  typedef Result return_type;
  function() = default;
  function(Result (*fn)(Args...)) : function_(fn) {};
  Result operator()(Args... a) {
   return function_(a...);
  }
  function& operator=(Result (*fn)(Args...)) {
   function_ = fn;
   return *this;
  }
};

// tuple
template <typename... T>
class tuple;

template <typename HEAD, typename... LIST>
class tuple<HEAD, LIST...> : public tuple<LIST...> {
 public:
  HEAD value;
  tuple(HEAD head, LIST... list) : tuple<LIST...>(list...), value(head) {};
};

template <>
class tuple<> {};

// tuple get
template <int index, typename... T>
class _tuple_type;

template <int index, typename HEAD, typename... LIST>
class _tuple_type<index, tuple<HEAD, LIST...>> {
 public:
  typedef typename _tuple_type<index - 1, tuple<LIST...>>::value_type value_type;
  typedef typename _tuple_type<index - 1, tuple<LIST...>>::tuple_type tuple_type;
};

template <typename HEAD, typename... LIST>
class _tuple_type<0, tuple<HEAD, LIST...>> {
 public:
  typedef HEAD value_type;
  typedef tuple<HEAD, LIST...> tuple_type;
};

template <int index, typename HEAD, typename... LIST>
typename _tuple_type<index, tuple<HEAD, LIST...>>::value_type get(tuple<HEAD, LIST...> t) {
 typedef typename _tuple_type<index, tuple<HEAD, LIST...>>::value_type value_type;
 typedef typename _tuple_type<index, tuple<HEAD, LIST...>>::tuple_type tuple_type;
 value_type rv = ((tuple_type)t).value;
 return rv;
}

// bind
template <size_t...>
class _tuple_index {};

template <size_t n, size_t... indexs>
class _make_indexs : public _make_indexs<n - 1, n - 1, indexs...> {};

template<size_t... indexs>
class _make_indexs<0, indexs...> {
 public:
  typedef _tuple_index<indexs...> index_type;
};

namespace placeholders {

template <size_t index>
class _placeholders {};

_placeholders<0> _1;
_placeholders<1> _2;
_placeholders<2> _3;
_placeholders<3> _4;
_placeholders<4> _5;
_placeholders<5> _6;
_placeholders<6> _7;
_placeholders<7> _8;
_placeholders<8> _9;
_placeholders<9> _10;

template <typename... RArgs>
class _unwrap_tuple {
 public:
  tuple<RArgs...> r_args; 
  _unwrap_tuple(tuple<RArgs...> r_args) : r_args(r_args) {};
  template <typename R>
  R operator[](R r) {
   return r;
  }
  template <size_t index>
  auto operator[](placeholders::_placeholders<index>) {
   return get<index>(r_args);
  }
};

};

template <typename Func, typename... Args>
class bind_t {
 public:
  typedef typename _make_indexs<sizeof...(Args)>::index_type _indexs;
  typedef typename Func::return_type return_type;
  Func func;
  tuple<Args...> args;
  bind_t(Func func, Args... args): func(func), args(args...) {}
  template <typename... RArgs>
  return_type operator()(RArgs&&... _r_args) {
   tuple<RArgs...> r_args = tuple<RArgs...>(_r_args...);
   return run(_indexs(), r_args);
  }
  template <size_t... Idx, typename... RArgs>
  return_type run(_tuple_index<Idx...>, tuple<RArgs...> r_args) {
   return func(unwrap_args<Idx>(r_args)...);
  }
  template <size_t index, typename... RArgs>
  auto unwrap_args(tuple<RArgs...> r_args) {
   placeholders::_unwrap_tuple<RArgs...> _u_a(r_args);
   auto _m_a = get<index>(args);
   return _u_a[_m_a];
  }
};

template <typename Func, typename... Args>
bind_t<Func, Args...> bind(Func& func, Args&&... args) {
 return bind_t<Func, Args...>(func, args...);
}

}; // end namespace dosk



// Test code
std::string test_func(int a, const char * b) {
 return std::to_string(a) + std::string(b);
}

std::string test_bind_args(int a, int b, int c, int d, int e) {
 return std::to_string(a) + std::to_string(b) + std::to_string(c) + std::to_string(d) + std::to_string(e);
}

int main() {
 // Test tuple
 dosk::tuple<int, const char *> t(123, "456");
 std::cout << dosk::get<0>(t) << dosk::get<1>(t) << std::endl;
 // Test function
 dosk::function<std::string(int, const char *)> closure_1 = test_func;
 std::cout << closure_1(123, "456") << std::endl;
 // Test bind
 dosk::function<std::string(int, int, int, int, int)> closure_2 = test_bind_args;
 auto binder = dosk::bind(closure_2, 1, dosk::placeholders::_2, 3, dosk::placeholders::_1, 5);
 std::cout << binder(4, 2, 0) << std::endl;
 return 0;
}


Related articles: